/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package commons.page.model;

import java.util.ArrayList;
import java.util.List;

/**
 * Represents the data presented by a UIData component, together with some state
 * information about the currently selected row within the datalist for use by
 * listeners on UIData components. This class allows managed bean code to avoid
 * binding directly to UIData components for typical uses.
 * <p>
 * Note that DataModel and its standard subclasses are not serializable, as
 * there is no state in a DataModel object itself that needs to be preserved
 * between render and restore-view. UIData components therefore do not store
 * their DataModel when serialized; they just evaluate their "value" EL
 * expression to refetch the object during the apply-request-values phase.
 * <p>
 * Because DataModel is not serializable, any managed bean that needs to be
 * serialized and which has a member of type DataModel should therefore mark
 * that member transient. If there is a need to preserve the datalist contained
 * within the DataModel then ensure a reference to that list is stored in a
 * non-transient member, or use a custom serialization method that explicitly
 * serializes dataModel.getWrappedData.
 * 
 * See Javadoc of <a
 * href="http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/index.html">JSF
 * Specification</a> for more.
 * 
 * @author Thomas Spiegl (latest modification by $Author: grantsmith $)
 * @version $Revision: 472558 $ $Date: 2006-11-08 18:36:53 +0100 (Mi, 08 Nov
 *          2006) $
 */
public abstract class DataModel {
	// FIELDS
	private List _listeners;

	// METHODS
	public void addDataModelListener(DataModelListener listener) {
		if (listener == null)
			throw new NullPointerException("listener");
		if (_listeners == null) {
			_listeners = new ArrayList();
		}
		_listeners.add(listener);
	}

	public DataModelListener[] getDataModelListeners() {
		if (_listeners == null) {
			return new DataModelListener[0];
		}
		return (DataModelListener[]) _listeners.toArray(new DataModelListener[_listeners.size()]);
	}

	/**
	 * Return the number of rows of data available.
	 * <p>
	 * If the number of rows of data available is not known then -1 is returned.
	 * This may happen for DataModels that wrap sources of data such as
	 * java.sql.ResultSet that provide an iterator to access the "next item"
	 * rather than a fixed-size collection of data.
	 */
	abstract public int getRowCount();

	/**
	 * Return the object associated with the current row index.
	 * <p>
	 * Method isRowAvailable may be called before attempting to access this
	 * method, to ensure that the data is available.
	 * 
	 * @throws RuntimeException
	 *             subclass of some kind if the current row index is not within
	 *             the range of the current wrappedData property.
	 */
	abstract public Object getRowData();

	/**
	 * Get the current row index.
	 */
	abstract public int getRowIndex();

	/**
	 * Get the entire collection of data associated with this component. Note
	 * that the actual type of the returned object depends upon the concrete
	 * subclass of DataModel; the object will represent an "ordered sequence of
	 * components", but may be implemented as an array, java.util.List,
	 * java.sql.ResultSet or other similar types.
	 */
	abstract public Object getWrappedData();

	/**
	 * Returns true if a call to getRowData will return a valid object.
	 */
	abstract public boolean isRowAvailable();

	public void removeDataModelListener(DataModelListener listener) {
		if (listener == null)
			throw new NullPointerException("listener");
		if (_listeners != null) {
			_listeners.remove(listener);
		}
	}

	/**
	 * Set the current row index. This affects the behaviour of the getRowData
	 * method in particular.
	 * 
	 * Parameter rowIndex may be -1 to indicate "no row", or may be a value
	 * between 0 and getRowCount()-1.
	 */
	abstract public void setRowIndex(int rowIndex);

	/**
	 * Set the entire list of data associated with this component. Note that the
	 * actual type of the provided object must match the expectations of the
	 * concrete subclass of DataModel. See getWrappedData.
	 */
	abstract public void setWrappedData(Object data);
}
